//
//  LogicSimTests.m
//  LogicSimTests
//
//  Created by Jeremy Pereira on 03/04/2012.
//  Copyright (c) 2012 Jeremy Pereira. All rights reserved.
//

#import "LogicSimTests.h"
#import "LGSAndGate.h"
#import "LGSOrGate.h"
#import "LGSXorGate.h"
#import "LGSNotGate.h"
#import "LGSEdge.h"
#import "LGSSplitter.h"
#import "LGSError.h"
#import "LGSExecutor.h"
#import "LGS1Const.h"
#import "LGS0Const.h"
#import "LGSSwitch.h"
#import "LGSHexDigit.h"

@implementation LogicSimTests

- (void)setUp
{
    [super setUp];
}

- (void)tearDown
{
    // Tear-down code here.
    
    [super tearDown];
}

-(void) testAnd
{
    LGSAndGate* andGate = [[LGSAndGate alloc] init];
    LGSEdge* input1 = [[LGSEdge alloc] init];
    LGSEdge* input2 = [[LGSEdge alloc] init];
    LGSEdge* output = [[LGSEdge alloc] init];
    
    [andGate addInput: input1 forName: [LGSAndGate inputAName]];
    [andGate addInput: input2 forName: [LGSAndGate inputBName]];
    [andGate addOutput: output forName: [LGSAndGate outputName]];
    [input1 setValue: YES affectedNodes: nil];
    [input2 setValue: YES affectedNodes: nil];
    [andGate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"Output should be true");
    [input1 setValue: YES affectedNodes: nil];
    [input2 setValue: NO affectedNodes: nil];
    [andGate evaluateAffectedNodes: nil];
    STAssertFalse([output value], @"Output should be false");
    [input1 setValue: NO affectedNodes: nil];
    [input2 setValue: YES affectedNodes: nil];
    [andGate evaluateAffectedNodes: nil];
    STAssertFalse([output value], @"Output should be false");
    [input1 setValue: NO affectedNodes: nil];
    [input2 setValue: NO affectedNodes: nil];
    [andGate evaluateAffectedNodes: nil];
    STAssertFalse([output value], @"Output should be false");
}

-(void) testOr
{
    LGSOrGate* orGate = [[LGSOrGate alloc] init];
    LGSEdge* input1 = [[LGSEdge alloc] init];
    LGSEdge* input2 = [[LGSEdge alloc] init];
    LGSEdge* output = [[LGSEdge alloc] init];
    
    [orGate addInput: input1 forNameString: @"a"];
    [orGate addInput: input2 forNameString: @"b"];
    [orGate addOutput: output forNameString: @"output"];
    [input1 setValue: YES affectedNodes: nil];
    [input2 setValue: YES affectedNodes: nil];
    [orGate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"Output should be true");
    [input1 setValue: YES affectedNodes: nil];
    [input2 setValue: NO affectedNodes: nil];
    [orGate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"Output should be true");
    [input1 setValue: NO affectedNodes: nil];
    [input2 setValue: YES affectedNodes: nil];
    [orGate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"Output should be true");
    [input1 setValue: NO affectedNodes: nil];
    [input2 setValue: NO affectedNodes: nil];
    [orGate evaluateAffectedNodes: nil];
    STAssertFalse([output value], @"Output should be false");
}

-(void) testXor
{
    LGSXorGate* xorGate = [[LGSXorGate alloc] initWithNameString: @"xor gate"];
    LGSEdge* input1 = [[LGSEdge alloc] init];
    LGSEdge* input2 = [[LGSEdge alloc] init];
    LGSEdge* output = [[LGSEdge alloc] init];
    
    [xorGate addInput: input1 forNameString: @"a"];
    [xorGate addInput: input2 forNameString: @"b"];
    [xorGate addOutput: output forNameString: @"output"];
    [input1 setValue: YES affectedNodes: nil];
    [input2 setValue: YES affectedNodes: nil];
    [xorGate evaluateAffectedNodes: nil];
    STAssertFalse([output value], @"Output should be false");
    [input1 setValue: YES affectedNodes: nil];
    [input2 setValue: NO affectedNodes: nil];
    [xorGate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"Output should be true");
    [input1 setValue: NO affectedNodes: nil];
    [input2 setValue: YES affectedNodes: nil];
    [xorGate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"Output should be true");
    [input1 setValue: NO affectedNodes: nil];
    [input2 setValue: NO affectedNodes: nil];
    [xorGate evaluateAffectedNodes: nil];
    STAssertFalse([output value], @"Output should be false");
}


-(void) testNot
{
    LGSNotGate* notGate = [[LGSNotGate alloc] init];
    LGSEdge* input = [[LGSEdge alloc] init];
    LGSEdge* output = [[LGSEdge alloc] init];
    
    [notGate addInput: input forNameString: @"in"];
    [notGate addOutput: output forNameString: @"out"];
    [input setValue: YES affectedNodes: nil];
    [notGate evaluateAffectedNodes: nil];
    STAssertFalse([output value], @"Output should be false");
    [input setValue: NO affectedNodes: nil];
    [notGate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"Output should be true");
}

-(void) testSplitter
{
    LGSSplitter* splitter = [[LGSSplitter alloc] init];
    LGSEdge* input = [[LGSEdge alloc] init];
    LGSEdge* output1 = [[LGSEdge alloc] init];
    LGSEdge* output2 = [[LGSEdge alloc] init];
    
    [splitter addInput: input forNameString: @"in"];
    [splitter addOutput: output1 forNameString: @"out"];
    [splitter addOutput: output2 forNameString: @"out"];
    [input setValue: YES affectedNodes: nil];
    [splitter evaluateAffectedNodes: nil];
    STAssertTrue([output1 value], @"Output1 should be true");
    STAssertTrue([output2 value], @"Output2 should be true");
    [input setValue: NO affectedNodes: nil];
    [splitter evaluateAffectedNodes: nil];
    STAssertFalse([output1 value], @"Output1 should be false");
    STAssertFalse([output2 value], @"Output2 should be false");
}

-(void) testAffectedNodes
{
    LGSAndGate* andGate = [[LGSAndGate alloc] init];
    [andGate setLabel: @"and"];
    LGSNotGate* notGate = [[LGSNotGate alloc] init];
    [notGate setLabel: @"not"];
    LGSEdge* input1 = [[LGSEdge alloc] init];
    LGSEdge* input2 = [[LGSEdge alloc] init];
    LGSEdge* connector = [[LGSEdge alloc] init];
    LGSEdge* output = [[LGSEdge alloc] init];
    [andGate addInput: input1 forNameString: @"a"];
    [andGate addInput: input2 forNameString: @"b"];
    [andGate addOutput: connector forNameString: @"output"];
    [notGate addInput: connector forNameString: @"in"];
    [notGate addOutput: output forNameString: @"out"];
    NSMutableOrderedSet* affectedNodes = [[NSMutableOrderedSet alloc] init];
    [input1 setValue: true affectedNodes: affectedNodes];
    [input2 setValue: true affectedNodes: affectedNodes];
    STAssertTrue([affectedNodes count] == 1, 
                 @"Wrong afected nodes %@", affectedNodes);
    [andGate evaluateAffectedNodes: affectedNodes];
    STAssertTrue([affectedNodes count] == 2, 
                 @"Wrong afected nodes %@", affectedNodes);
    NSLog(@"Affected nodes %@", affectedNodes);
}

-(void) testExecutor
{
    LGSAndGate* aAndNotB = [[LGSAndGate alloc] init];
    [aAndNotB setLabel: @"a & ~b"];
    LGSAndGate* bAndNotA = [[LGSAndGate alloc] init];
    [bAndNotA setLabel: @"~a & b"];
    LGSAndGate* aAndB = [[LGSAndGate alloc] init];
    [aAndB setLabel: @"a & b"];
    LGSNotGate* notA = [[LGSNotGate alloc] init];
    [notA setLabel: @"~a"];
    LGSNotGate* notB = [[LGSNotGate alloc] init];
    [notB setLabel: @"~b"];
    LGSOrGate* or = [[LGSOrGate alloc] init];
    [or setLabel: @"|"];
    LGSSplitter* aSplitter = [[LGSSplitter alloc] init];
    LGSSplitter* bSplitter = [[LGSSplitter alloc] init];
    [aSplitter setLabel: @"a"];
    [bSplitter setLabel: @"b"];
    LGSEdge* a = [[LGSEdge alloc] init];
    LGSEdge* b = [[LGSEdge alloc] init];
    [aSplitter addInput: a forNameString: @"in"];
    [bSplitter addInput: b forNameString: @"in"];
    [aSplitter connect: aAndNotB fromString: @"out" toString: @"a"];
    [aSplitter connect: notA fromString: @"out" toString: @"in"];
    [aSplitter connect: aAndB fromString: @"out" toString: @"a"];
    [bSplitter connect: bAndNotA fromString: @"out" toString: @"a"];
    [bSplitter connect: notB fromString: @"out" toString: @"in"];
    [bSplitter connect: aAndB fromString: @"out" toString: @"b"];
    [notA connect: bAndNotA fromString: @"out" toString: @"b"];
    [notB connect: aAndNotB fromString: @"out" toString: @"b"];
    [aAndNotB connect: or fromString: @"output" toString: @"a"];
    [bAndNotA connect: or fromString: @"output" toString: @"b"];
    LGSEdge* bit = [[LGSEdge alloc] init];
    LGSEdge* carry = [[LGSEdge alloc] init];
    [aAndB addOutput: carry forNameString: @"output"];
    [or addOutput: bit forNameString: @"output"];
    
    NSMutableOrderedSet* nodesToEvaluate = [[NSMutableOrderedSet alloc] init];
    [a setValue: true affectedNodes: nodesToEvaluate];
    [b setValue: true affectedNodes: nodesToEvaluate];
    NSLog(@"Nodes to evaluate: %@", nodesToEvaluate);
    LGSExecutor* executor = [[LGSExecutor alloc] init];
    [executor setTrace: true];
    [executor evaluateWithInitialSet: nodesToEvaluate
                       maxIterations: 100];
    STAssertFalse([bit value], @"Bit should be 0 (1 + 1 = 10)");
    STAssertTrue([carry value], @"Carry should be 1 (1 + 1 = 10)");
    [nodesToEvaluate removeAllObjects];
    [a setValue: false affectedNodes: nodesToEvaluate];
    [executor evaluateWithInitialSet: nodesToEvaluate
                       maxIterations: 100];
    STAssertTrue([bit value], @"Bit should be 1 (0 + 1 = 1)");
    STAssertFalse([carry value], @"Carry should be 0 (0 + 1 = 01)");
    [nodesToEvaluate removeAllObjects];
    [b setValue: false affectedNodes: nodesToEvaluate];
    [executor evaluateWithInitialSet: nodesToEvaluate
                       maxIterations: 100];
    STAssertFalse([bit value], @"Bit should be 0 (0 + 0 = 0)");
    STAssertFalse([carry value], @"Carry should be 0 (0 + 0 = 0)");
    [nodesToEvaluate removeAllObjects];
    [a setValue: true affectedNodes: nodesToEvaluate];
    [executor evaluateWithInitialSet: nodesToEvaluate
                       maxIterations: 100];
    STAssertTrue([bit value], @"Bit should be 1 (0 + 1 = 1)");
    STAssertFalse([carry value], @"Carry should be 0 (0 + 1 = 01)");
}

-(void) test1Const
{
    LGS1Const* gate = [[LGS1Const alloc] init];
    LGSEdge* output = [[LGSEdge alloc] init];
    
    [gate addOutput: output forNameString: @"out"];
    [gate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"");
    [gate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"");    
}

-(void) test0Const
{
    LGS0Const* gate = [[LGS0Const alloc] init];
    LGSEdge* output = [[LGSEdge alloc] init];
    
    [gate addOutput: output forNameString: @"out"];
    [gate evaluateAffectedNodes: nil];
    
    STAssertFalse([output value], @"");
    [gate evaluateAffectedNodes: nil];
    STAssertFalse([output value], @"");    
}


-(void) testSwitch
{
    LGSSwitch* gate = [[LGSSwitch alloc] init];
    LGSEdge* input0 = [[LGSEdge alloc] init];
    LGSEdge* input1 = [[LGSEdge alloc] init];
    LGSEdge* selector = [[LGSEdge alloc] init];
    LGSEdge* output = [[LGSEdge alloc] init];
    
    [gate addOutput: output forNameString: @"out"];
    [gate addInput: input0 forNameString: @"a"];
    [gate addInput: input1 forNameString: @"b"];
    [gate addInput: selector forNameString: @"select"];
    
	[input0 setValue: true affectedNodes: nil];
	[input1 setValue: false affectedNodes: nil];
	[selector setValue: false affectedNodes: nil];
    [gate evaluateAffectedNodes: nil];
    STAssertTrue([output value], @"");
    [selector setValue: true affectedNodes: nil];
    [gate evaluateAffectedNodes: nil];
    STAssertFalse([output value], @"");    
}

-(void) testHexDigit
{
    LGSHexDigit* hexDigit =[[LGSHexDigit alloc] init];
    LGSEdge* bit0 = [[LGSEdge alloc] init];
    LGSEdge* bit1 = [[LGSEdge alloc] init];
    LGSEdge* bit2 = [[LGSEdge alloc] init];
    LGSEdge* bit3 = [[LGSEdge alloc] init];
    [hexDigit addOutput: bit0 forNameString: @"bit[0]"];
    [hexDigit addOutput: bit1 forNameString: @"bit[1]"];
    [hexDigit addOutput: bit2 forNameString: @"bit[2]"];
    [hexDigit addOutput: bit3 forNameString: @"bit[3]"];
    [hexDigit setDigit: 0xA];
    [hexDigit evaluateAffectedNodes: nil];
	STAssertTrue([bit1 value], @"Bit 1 is wrong in %@", hexDigit);
	STAssertTrue([bit3 value], @"Bit 3 is wrong in %@", hexDigit);
	STAssertFalse([bit0 value], @"Bit 0 is wrong in %@", hexDigit);
	STAssertFalse([bit2 value], @"Bit 2 is wrong in %@", hexDigit);
}

@end
